/**
 * 函数相关中间件
 *
 * Created by snowingsea on 2020/03/06.
 */
const tests = require('./../../models/tests');
const testCaseGroup = require('./../../models/test-case-groups');
const projectVersions = require('./../../models/project-versions');
const projects = require('./../../models/projects');
const files = require('./../../models/files');
const PhoenixError = require('./../../models/phoenix-error');
const utility = require('./../../models/utility');
const breakPoints = require('./../../models/break-points');

/**
 * 判断URL参数中测试用例Id是否存在
 * @param req
 * @param res
 * @param next
 */
function requireTestCaseExist(req, res, next) {
  const { testId } = req.params;
  if (!testId) {
    next(new PhoenixError('invalid request', 'Missing parameters. "testId" is required'));
    return;
  }
  tests.getTest(testId)
    .then((testCase) => {
      if (!testCase) {
        return Promise.reject(new PhoenixError('not found', 'Not found, testCase not found'));
      }
      req.$testCase = testCase;
      const { projectId, versionId, fileId } = testCase;
      const promises = [
        projects.getProject(projectId),
        projectVersions.getVersion(versionId),
        files.getFile(fileId),
      ];
      return Promise.all(promises);
    })
    .then(([project, projectVersion, file]) => {
      if (!projectVersion) {
        return Promise.reject(new PhoenixError('not found', 'Not found, projectVersion not found'));
      }
      if (!project) {
        return Promise.reject(new PhoenixError('not found', 'Not found, project not found'));
      }
      if (!file) {
        return Promise.reject(new PhoenixError('not found', 'Not found, file not found'));
      }
      req.$project = project;
      req.$projectVersion = projectVersion;
      req.$file = file;
      return Promise.resolve();
    })
    .then(() => {
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 清理测试用例数据
 * @param {Object} testData 测试用例数据
 */
function clearTestCaseData(testData, path = '') {
  if (!testData || !utility.isObjectType(testData)) {
    return;
  }
  // if (testData['@result']) {
  //   if (!testData['@value']) {
  //     testData['@value'] = testData['@result'];
  //   }
  // }
  // 绝对地址存在variable类型和生成的测试用例中类型不一致的问题，所以绝对地址中的@type不能移除
  Object.keys(testData).forEach((key) => {
    if (key.startsWith('@')
      && !['@value', '@init', '@ctor'].includes(key)
      && !(path.startsWith('fixedAddrs')
      && (key === '@type' || key === '@length' || key === '@elementType'))) {
      delete testData[key];
    }
  });
  if (!testData['@value']) {
    delete testData['@value'];
  }
  Object.keys(testData).forEach((key) => {
    clearTestCaseData(testData[key], path ? `${path}.${key}` : key);
    const item = testData[key];
    if (!item || (utility.isObjectType(item) && Object.keys(item).length === 0)) {
      delete testData[key];
    }
  });
}

/**
 * 解析测试用例数据
 * @param req
 * @param res
 * @param next
 */
function parseTestCaseData(req, res, next) {
  Promise.resolve()
    .then(async () => {
      const copyId = req.query.copyId || req.body.copyId;
      if (copyId) {
        const copyIds = [].concat(copyId);
        const testCases = await Promise.all(copyIds.map(tests.getTest));
        for (let index = 0; index < testCases.length; index += 1) {
          const tc = testCases[index];
          if (!tc) {
            throw new PhoenixError('not found', `test case ${copyIds[index]} not found`);
          }
        }
        return testCases;
      }
      return null;
    })
    .then((testCases) => {
      testCases = testCases || [{ data: {}, description: '' }];
      testCases = testCases.map((test) => {
        const data = req.query.data || req.body.data;
        const description = req.query.description || req.body.description;
        if (data) {
          try {
            test.data = JSON.parse(data);
          } catch (e) {
            test.data = test.data || {};
          }
        }
        if (description) {
          test.description = description;
        }
        clearTestCaseData(test.data);
        return test;
      });
      req.$testCasesData = testCases;
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * 进行测试用例(编组)列表解析重组
 * @param req
 * @param res
 * @param next
 */
function parseTestcaseListQuery(req, res, next) {
  ['testIds', 'groupIds'].forEach((key) => {
    req.query[key] = utility.parseArray(req.query, key);
    req.body[key] = utility.parseArray(req.body, key);
    if (!req.query[key]) {
      // make sure req.query.key ALWAYS exists
      req.query[key] = req.body[key] ? req.body[key] : [];
    } else if (req.body[key]) {
      req.query[key] = req.query[key].concat(req.body[key]);
    }
  });
  next();
}

/**
 * 获取测试用例以及编组下测试用例
 * @param req
 * @param res
 * @param next
 */
function getTests(req, res, next) {
  const promises = [];
  req.query.groupIds.forEach((groupId) => {
    promises.push(testCaseGroup.getAllTestsByGroupId(groupId));
  });
  Promise.all(promises)
    .then((testLists) => {
      let { testIds } = req.query;
      testLists.forEach((testList) => {
        testIds = testIds.concat(testList);
      });
      return tests.getTests({ testIds });
    })
    .then((tests) => {
      req.$testCases = tests;
      next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

function requireBreakPointExist(req, res, next) {
  const { breakPointId } = req.params;
  if (!breakPointId) {
    next(new PhoenixError('invalid request', 'Missing parameters. "breakPointId" is required'));
    return;
  }
  breakPoints.getBreakPoint(breakPointId)
    .then((breakPoint) => {
      if (!breakPoint) {
        return Promise.reject(new PhoenixError('not found', 'Not found, breakPoint not found'));
      }
      req.$breakPoint = breakPoint;
      return next();
    })
    .catch((err) => {
      next(new PhoenixError(err));
    });
}

/**
 * Export model definition object.
 */
module.exports = {
  requireTestCaseExist,
  requireBreakPointExist,
  parseTestCaseData,
  parseTestcaseListQuery,
  getTests,
};
